home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mint108s.zoo / dosdir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-02  |  29.7 KB  |  1,315 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith.
  3. Copyright 1992,1993 Atari Corporation.
  4. All rights reserved.
  5. */
  6.  
  7. /* DOS directory functions */
  8.  
  9. #include "mint.h"
  10.  
  11. /* change to a new drive: should always return a map of valid drives */
  12.  
  13. long ARGS_ON_STACK
  14. d_setdrv(d)
  15.     int d;
  16. {
  17.     long r;
  18.     extern long dosdrvs;    /* in filesys.c */
  19.  
  20.     r = drvmap() | dosdrvs | PSEUDODRVS;
  21.  
  22.     TRACE(("Dsetdrv(%d)", d));
  23.     if (d < 0 || d >= NUM_DRIVES || (r & (1L << d)) == 0) {
  24.         DEBUG(("Dsetdrv: invalid drive %d", d));
  25.         return r;
  26.     }
  27.  
  28.     curproc->base->p_defdrv = curproc->curdrv = d;
  29.     return r;
  30. }
  31.  
  32.  
  33. long ARGS_ON_STACK
  34. d_getdrv()
  35. {
  36.     TRACE(("Dgetdrv"));
  37.     return curproc->curdrv;
  38. }
  39.  
  40. long ARGS_ON_STACK
  41. d_free(buf, d)
  42.     long *buf;
  43.     int d;
  44. {
  45.     fcookie *dir = 0;
  46.     extern int aliasdrv[];
  47.  
  48.     TRACE(("Dfree(%d)", d));
  49.  
  50. /* drive 0 means current drive, otherwise it's d-1 */
  51.     if (d)
  52.         d = d-1;
  53.     else
  54.         d = curproc->curdrv;
  55.  
  56. /* Hack to make programs (like df) which use drive
  57.  * information from Fxattr() work more often.
  58.  * BUG: this works only if the drive is a root,
  59.  * a current directory or an alias, or one of the
  60.  * standard drives.
  61.  */
  62.     if (d < 0 || d >= NUM_DRIVES) {
  63.         int i;
  64.  
  65.         for (i = 0; i < NUM_DRIVES; i++) {
  66.             if (aliasdrv[i] == d) {
  67.                 d = i;
  68.                 goto aliased;
  69.             }
  70.             if (curproc->curdir[i].dev == d) {
  71.                 dir = &curproc->curdir[i];
  72.             } else if (curproc->root[i].dev == d) {
  73.                 dir = &curproc->root[i];
  74.             }
  75.         }
  76.         if (dir && dir->fs) {
  77.             return (*dir->fs->dfree)(dir, buf);
  78.         }
  79.         return EDRIVE;
  80.     }
  81.  
  82. /* check for a media change -- we don't care much either way, but it
  83.  * does keep the results more accurate
  84.  */
  85.     (void)disk_changed(d);
  86.  
  87. aliased:
  88.  
  89. /* use current directory, not root, since it's more likely that
  90.  * programs are interested in the latter (this makes U: work much
  91.  * better)
  92.  */
  93.     dir = &curproc->curdir[d];
  94.     if (!dir->fs) {
  95.         DEBUG(("Dfree: bad drive"));
  96.         return EDRIVE;
  97.     }
  98.  
  99.     return (*dir->fs->dfree)(dir, buf);
  100. }
  101.  
  102. long ARGS_ON_STACK
  103. d_create(path)
  104.     const char *path;
  105. {
  106.     fcookie dir;
  107.     long r;
  108.     char temp1[PATH_MAX];
  109.  
  110.     TRACE(("Dcreate(%s)", path));
  111.  
  112.     r = path2cookie(path, temp1, &dir);
  113.     if (r) {
  114.         DEBUG(("Dcreate(%s): returning %ld", path, r));
  115.         return r;    /* an error occured */
  116.     }
  117. /* check for write permission on the directory */
  118.     r = dir_access(&dir, S_IWOTH);
  119.     if (r) {
  120.         DEBUG(("Dcreate(%s): access to directory denied",path));
  121.         release_cookie(&dir);
  122.         return r;
  123.     }
  124.     r = (*dir.fs->mkdir)(&dir, temp1, DEFAULT_DIRMODE & ~curproc->umask);
  125.     release_cookie(&dir);
  126.     return r;
  127. }
  128.  
  129. long ARGS_ON_STACK
  130. d_delete(path)
  131.     const char *path;
  132. {
  133.     fcookie parentdir, targdir;
  134.     long r;
  135.     PROC *p;
  136.     int i;
  137.     XATTR xattr;
  138.     char temp1[PATH_MAX];
  139.  
  140.     TRACE(("Ddelete(%s)", path));
  141.  
  142.     r = path2cookie(path, temp1, &parentdir);
  143.  
  144.     if (r) {
  145.         DEBUG(("Ddelete(%s): error %lx", path, r));
  146.         release_cookie(&parentdir);
  147.         return r;
  148.     }
  149. /* check for write permission on the directory which the target
  150.  * is located
  151.  */
  152.     if ((r = dir_access(&parentdir, S_IWOTH)) != 0) {
  153.         DEBUG(("Ddelete(%s): access to directory denied", path));
  154.         release_cookie(&parentdir);
  155.         return r;
  156.     }
  157.  
  158. /* now get the info on the file itself */
  159.  
  160.     r = relpath2cookie(&parentdir, temp1, NULL, &targdir, 0);
  161.     if (r) {
  162. bailout:
  163.         release_cookie(&parentdir);
  164.         DEBUG(("Ddelete: error %ld on %s", r, path));
  165.         return r;
  166.     }
  167.     if ((r = (*targdir.fs->getxattr)(&targdir, &xattr)) != 0) {
  168.         release_cookie(&targdir);
  169.         goto bailout;
  170.     }
  171.  
  172. /* if the "directory" is a symbolic link, really unlink it */
  173.     if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  174.         r = (*parentdir.fs->remove)(&parentdir, temp1);
  175.     } else if ( (xattr.mode & S_IFMT) != S_IFDIR ) {
  176.         DEBUG(("Ddelete: %s is not a directory", path));
  177.         r = EPTHNF;
  178.     } else {
  179.  
  180. /* don't delete anyone else's root or current directory */
  181.         for (p = proclist; p; p = p->gl_next) {
  182.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  183.             continue;
  184.         for (i = 0; i < NUM_DRIVES; i++) {
  185.             if (samefile(&targdir, &p->root[i])) {
  186.                 DEBUG(("Ddelete: directory %s is a root directory",
  187.                     path));
  188. noaccess:
  189.                 release_cookie(&targdir);
  190.                 release_cookie(&parentdir);
  191.                 return EACCDN;
  192.             } else if (samefile(&targdir, &p->curdir[i])) {
  193.                 if (i == p->curdrv && p != curproc) {
  194.                     DEBUG(("Ddelete: directory %s is in use",
  195.                         path));
  196.                     goto noaccess;
  197.                 } else {
  198.                     release_cookie(&p->curdir[i]);
  199.                     dup_cookie(&p->curdir[i], &p->root[i]);
  200.                 } 
  201.             }
  202.         }
  203.         }
  204.         release_cookie(&targdir);
  205.         r = (*parentdir.fs->rmdir)(&parentdir, temp1);
  206.     }
  207.     release_cookie(&parentdir);
  208.     return r;
  209. }
  210.  
  211. long ARGS_ON_STACK
  212. d_setpath(path)
  213.     const char *path;
  214. {
  215.     fcookie dir;
  216.     int drv = curproc->curdrv;
  217.     int i;
  218.     char c;
  219.     long r;
  220.     XATTR xattr;
  221.  
  222.     TRACE(("Dsetpath(%s)", path));
  223.  
  224.     r = path2cookie(path, follow_links, &dir);
  225.  
  226.     if (r) {
  227.         DEBUG(("Dsetpath(%s): returning %ld", path, r));
  228.         return r;
  229.     }
  230.  
  231.     if (path[0] && path[1] == ':') {
  232.         c = *path;
  233.         if (c >= 'a' && c <= 'z')
  234.             drv = c-'a';
  235.         else if (c >= 'A' && c <= 'Z')
  236.             drv = c-'A';
  237.     }
  238.  
  239.     r = (*dir.fs->getxattr)(&dir, &xattr);
  240.  
  241.     if (r < 0) {
  242.         DEBUG(("Dsetpath: file '%s': attributes not found", path));
  243.         release_cookie(&dir);
  244.         return r;
  245.     }
  246.  
  247.     if (!(xattr.attr & FA_DIR)) {
  248.         DEBUG(("Dsetpath(%s): not a directory",path));
  249.         release_cookie(&dir);
  250.         return EPTHNF;
  251.     }
  252.  
  253. /*
  254.  * watch out for symbolic links; if c:\foo is a link to d:\bar, then
  255.  * "cd c:\foo" should also change the drive to d:
  256.  */
  257.     if (drv != UNIDRV && dir.dev != curproc->root[drv].dev) {
  258.         for (i = 0; i < NUM_DRIVES; i++) {
  259.             if (curproc->root[i].dev == dir.dev &&
  260.                 curproc->root[i].fs == dir.fs) {
  261.                 if (drv == curproc->curdrv)
  262.                     curproc->curdrv = i;
  263.                 drv = i;
  264.                 break;
  265.             }
  266.         }
  267.     }
  268.     release_cookie(&curproc->curdir[drv]);
  269.     curproc->curdir[drv] = dir;
  270.     return 0;
  271. }
  272.  
  273. /* jr: like d_getpath, except that the caller provides a limit
  274.    for the max. number of characters to be put into the buffer.
  275.    Inspired by POSIX.1, getcwd(), 5.2.2 */
  276.  
  277. long ARGS_ON_STACK
  278. d_getcwd(path, drv, size)
  279.     char *path;
  280.     int drv, size;
  281. {
  282.     fcookie *dir, *root;
  283.     long r;
  284.     char buf[PATH_MAX];
  285.     FILESYS *fs;
  286.  
  287.     TRACE(("Dgetcwd(%c, %d)", drv + '@', size));
  288.     if (drv < 0 || drv > NUM_DRIVES)
  289.         return EDRIVE;
  290.  
  291.     drv = (drv == 0) ? curproc->curdrv : drv-1;
  292.  
  293.     root = &curproc->root[drv];
  294.  
  295.     if (!root->fs) {    /* maybe not initialized yet? */
  296.         changedrv(drv);
  297.         root = &curproc->curdir[drv];
  298.         if (!root->fs)
  299.             return EDRIVE;
  300.     }
  301.     fs = root->fs;
  302.     dir = &curproc->curdir[drv];
  303.  
  304.     if (!(fs->fsflags & FS_LONGPATH)) {
  305.         r = (*fs->getname)(root, dir, buf, PATH_MAX);
  306.         if (r) return r;
  307.         if (strlen(buf) < size) {
  308.             strcpy(path, buf);
  309.             return 0;
  310.         } else {
  311.             return ERANGE;
  312.         }
  313.     }
  314.     return (*fs->getname)(root, dir, path, size);
  315. }
  316.  
  317. long ARGS_ON_STACK
  318. d_getpath(path, drv)
  319.     char *path;
  320.     int drv;
  321. {
  322.     TRACE(("Dgetpath(%c)", drv + '@'));
  323.     return d_getcwd(path, drv, PATH_MAX);
  324. }
  325.  
  326. long ARGS_ON_STACK
  327. f_setdta(dta)
  328.     DTABUF *dta;
  329. {
  330.  
  331.     TRACE(("Fsetdta: %lx", dta));
  332.     curproc->dta = dta;
  333.     curproc->base->p_dta = (char *)dta;
  334.     return 0;
  335. }
  336.  
  337. long ARGS_ON_STACK
  338. f_getdta()
  339. {
  340.     long r;
  341.  
  342.     r = (long)curproc->dta;
  343.     TRACE(("Fgetdta: returning %lx", r));
  344.     return r;
  345. }
  346.  
  347. /*
  348.  * Fsfirst/next are actually implemented in terms of opendir/readdir/closedir.
  349.  */
  350.  
  351. long ARGS_ON_STACK
  352. f_sfirst(path, attrib)
  353.     const char *path;
  354.     int attrib;
  355. {
  356.     char *s, *slash;
  357.     FILESYS *fs;
  358.     fcookie dir, newdir;
  359.     DTABUF *dta;
  360.     DIR *dirh;
  361.     XATTR xattr;
  362.     long r;
  363.     int i, havelabel;
  364.     char temp1[PATH_MAX];
  365.  
  366.     TRACE(("Fsfirst(%s, %x)", path, attrib));
  367.  
  368.     r = path2cookie(path, temp1, &dir);
  369.  
  370.     if (r) {
  371.         DEBUG(("Fsfirst(%s): path2cookie returned %ld", path, r));
  372.         return r;
  373.     }
  374.  
  375. /*
  376.  * we need to split the last name (which may be a pattern) off from
  377.  * the rest of the path, even if FS_KNOPARSE is true
  378.  */
  379.     slash = 0;
  380.     s = temp1;
  381.     while (*s) {
  382.         if (*s == '\\')
  383.             slash = s;
  384.         s++;
  385.     }
  386.  
  387.     if (slash) {
  388.         *slash++ = 0;    /* slash now points to a name or pattern */
  389.         r = relpath2cookie(&dir, temp1, follow_links, &newdir, 0);
  390.         release_cookie(&dir);
  391.         if (r) {
  392.             DEBUG(("Fsfirst(%s): lookup returned %ld", path, r));
  393.             return r;
  394.         }
  395.         dir = newdir;
  396.     } else {
  397.         slash = temp1;
  398.     }
  399.  
  400. /* BUG? what if there really is an empty file name? */
  401.     if (!*slash) {
  402.         DEBUG(("Fsfirst: empty pattern"));
  403.         return EFILNF;
  404.     }
  405.  
  406.     fs = dir.fs;
  407.     dta = curproc->dta;
  408.  
  409. /* Now, see if we can find a DIR slot for the search. We use the following
  410.  * heuristics to try to avoid destroying a slot:
  411.  * (1) if the search doesn't use wildcards, don't bother with a slot
  412.  * (2) if an existing slot was for the same DTA address, re-use it
  413.  * (3) if there's a free slot, re-use it. Slots are freed when the
  414.  *     corresponding search is terminated.
  415.  */
  416.  
  417.     for (i = 0; i < NUM_SEARCH; i++) {
  418.         if (curproc->srchdta[i] == dta) {
  419.             dirh = &curproc->srchdir[i];
  420.             if (dirh->fc.fs) {
  421.                 (*dirh->fc.fs->closedir)(dirh);
  422.                 release_cookie(&dirh->fc);
  423.                 dirh->fc.fs = 0;
  424.             }
  425.             curproc->srchdta[i] = 0; /* slot is now free */
  426.         }
  427.     }
  428.  
  429. /* copy the pattern over into dta_pat into TOS 8.3 form */
  430. /* remember that "slash" now points at the pattern (it follows the last \,
  431.    if any)
  432.  */
  433.     copy8_3(dta->dta_pat, slash);
  434.  
  435. /* if attrib & FA_LABEL, read the volume label */
  436. /* BUG: the label date and time are wrong. Does it matter?
  437.  */
  438.     havelabel = 0;
  439.     if (attrib & FA_LABEL) {
  440.         r = (*fs->readlabel)(&dir, dta->dta_name, TOS_NAMELEN+1);
  441.         dta->dta_attrib = FA_LABEL;
  442.         dta->dta_time = dta->dta_date = 0;
  443.         dta->dta_size = 0;
  444.         dta->magic = EVALID;
  445.         if (r == 0 && !pat_match(dta->dta_name, dta->dta_pat))
  446.             r = EFILNF;
  447.         if (attrib == FA_LABEL)
  448.             return r;
  449.         else if (r == 0)
  450.             havelabel = 1;
  451.     }
  452.  
  453.     if (!havelabel && has_wild(slash) == 0) { /* no wild cards in pattern */
  454.         r = relpath2cookie(&dir, slash, follow_links, &newdir, 0);
  455.         if (r == 0) {
  456.             r = (*newdir.fs->getxattr)(&newdir, &xattr);
  457.             release_cookie(&newdir);
  458.         }
  459.         release_cookie(&dir);
  460.         if (r) {
  461.             DEBUG(("Fsfirst(%s): couldn't get file attributes",path));
  462.             return r;
  463.         }
  464.         dta->magic = EVALID;
  465.         dta->dta_attrib = xattr.attr;
  466.         dta->dta_time = xattr.mtime;
  467.         dta->dta_date = xattr.mdate;
  468.         dta->dta_size = xattr.size;
  469.         strncpy(dta->dta_name, slash, TOS_NAMELEN-1);
  470.         dta->dta_name[TOS_NAMELEN-1] = 0;
  471.         if (curproc->domain == DOM_TOS &&
  472.             !(fs->fsflags & FS_CASESENSITIVE))
  473.             strupr(dta->dta_name);
  474.         return 0;
  475.     }
  476.  
  477. /* There is a wild card. Try to find a slot for an opendir/readdir
  478.  * search. NOTE: we also come here if we were asked to search for
  479.  * volume labels and found one.
  480.  */
  481.     for (i = 0; i < NUM_SEARCH; i++) {
  482.         if (curproc->srchdta[i] == 0)
  483.             break;
  484.     }
  485.     if (i == NUM_SEARCH) {
  486.         int oldest = 0; long oldtime = curproc->srchtim[0];
  487.  
  488.         DEBUG(("Fsfirst(%s): having to re-use a directory slot!",path));
  489.         for (i = 1; i < NUM_SEARCH; i++) {
  490.             if (curproc->srchtim[i] < oldtime) {
  491.                 oldest = i;
  492.                 oldtime = curproc->srchtim[i];
  493.             }
  494.         }
  495.     /* OK, close this directory for re-use */
  496.         i = oldest;
  497.         dirh = &curproc->srchdir[i];
  498.         if (dirh->fc.fs) {
  499.             (*dirh->fc.fs->closedir)(dirh);
  500.             release_cookie(&dirh->fc);
  501.             dirh->fc.fs = 0;
  502.         }
  503.         curproc->srchdta[i] = 0;
  504.     }
  505.  
  506. /* check to see if we have read permission on the directory (and make
  507.  * sure that it really is a directory!)
  508.  */
  509.     r = dir_access(&dir, S_IROTH);
  510.     if (r) {
  511.         DEBUG(("Fsfirst(%s): access to directory denied (error code %ld)", path, r));
  512.         release_cookie(&dir);
  513.         return r;
  514.     }
  515.  
  516. /* set up the directory for a search */
  517.     dirh = &curproc->srchdir[i];
  518.     dirh->fc = dir;
  519.     dirh->index = 0;
  520.     dirh->flags = TOS_SEARCH;
  521.     r = (*dir.fs->opendir)(dirh, dirh->flags);
  522.     if (r != 0) {
  523.         DEBUG(("Fsfirst(%s): couldn't open directory (error %ld)",
  524.             path, r));
  525.         release_cookie(&dir);
  526.         return r;
  527.     }
  528.  
  529. /* mark the slot as in-use */
  530.     curproc->srchdta[i] = dta;
  531.  
  532. /* set up the DTA for Fsnext */
  533.     dta->index = i;
  534.     dta->magic = SVALID;
  535.     dta->dta_sattrib = attrib;
  536.  
  537. /* OK, now basically just do Fsnext, except that instead of ENMFIL we
  538.  * return EFILNF.
  539.  * NOTE: If we already have found a volume label from the search above,
  540.  * then we skip the f_snext and just return that.
  541.  */
  542.     if (havelabel)
  543.         return 0;
  544.  
  545.     r = f_snext();
  546.     if (r == ENMFIL) r = EFILNF;
  547.     if (r)
  548.         TRACE(("Fsfirst: returning %ld", r));
  549. /* release_cookie isn't necessary, since &dir is now stored in the
  550.  * DIRH structure and will be released when the search is completed
  551.  */
  552.     return r;
  553. }
  554.  
  555. /*
  556.  * Counter for Fsfirst/Fsnext, so that we know which search slots are
  557.  * least recently used. This is updated once per second by the code
  558.  * in timeout.c.
  559.  * BUG: 1/second is pretty low granularity
  560.  */
  561.  
  562. long searchtime;
  563.  
  564. long ARGS_ON_STACK
  565. f_snext()
  566. {
  567.     char buf[TOS_NAMELEN+1];
  568.     DTABUF *dta = curproc->dta;
  569.     FILESYS *fs;
  570.     fcookie fc;
  571.     int i;
  572.     DIR *dirh;
  573.     long r;
  574.     XATTR xattr;
  575.  
  576.     TRACE(("Fsnext"));
  577.  
  578.     if (dta->magic == EVALID) {
  579.         DEBUG(("Fsnext: DTA marked a failing search"));
  580.         return ENMFIL;
  581.     }
  582.     if (dta->magic != SVALID) {
  583.         DEBUG(("Fsnext: dta incorrectly set up"));
  584.         return EINVFN;
  585.     }
  586.  
  587.     i = dta->index;
  588.     dirh = &curproc->srchdir[i];
  589.     curproc->srchtim[i] = searchtime;
  590.  
  591.     fs = dirh->fc.fs;
  592.     if (!fs)        /* oops -- the directory got closed somehow */
  593.         return EINTRN;
  594.  
  595. /* BUG: f_snext and readdir should check for disk media changes */
  596.  
  597.     for(;;) {
  598.         r = (*fs->readdir)(dirh, buf, TOS_NAMELEN+1, &fc);
  599.  
  600.         if (r == ENAMETOOLONG) {
  601.             DEBUG(("Fsnext: name too long"));
  602.             continue;    /* TOS programs never see these names */
  603.         }
  604.         if (r != 0) {
  605. baderror:
  606.             if (dirh->fc.fs)
  607.                 (void)(*fs->closedir)(dirh);
  608.             release_cookie(&dirh->fc);
  609.             dirh->fc.fs = 0;
  610.             curproc->srchdta[i] = 0;
  611.             dta->magic = EVALID;
  612.             if (r != ENMFIL)
  613.                 DEBUG(("Fsnext: returning %ld", r));
  614.             return r;
  615.         }
  616.  
  617.         if (!pat_match(buf, dta->dta_pat))
  618.         {
  619.             release_cookie(&fc);
  620.             continue;    /* different patterns */
  621.         }
  622.  
  623.     /* check for search attributes */
  624.         r = (*fc.fs->getxattr)(&fc, &xattr);
  625.         if (r) {
  626.             DEBUG(("Fsnext: couldn't get file attributes"));
  627.             release_cookie(&fc);
  628.             goto baderror;
  629.         }
  630.     /* if the file is a symbolic link, try to find what it's linked to */
  631.         if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  632.             char linkedto[PATH_MAX];
  633.             r = (*fc.fs->readlink)(&fc, linkedto, PATH_MAX);
  634.             release_cookie(&fc);
  635.             if (r == 0) {
  636.             /* the "1" tells relpath2cookie that we read a link */
  637.                 r = relpath2cookie(&dirh->fc, linkedto,
  638.                     follow_links, &fc, 1);
  639.                 if (r == 0) {
  640.                 r = (*fc.fs->getxattr)(&fc, &xattr);
  641.                 release_cookie(&fc);
  642.                 }
  643.             }
  644.             if (r) {
  645.                 DEBUG(("Fsnext: couldn't follow link: error %ld",
  646.                     r));
  647.             }
  648.         } else {
  649.             release_cookie(&fc);
  650.         }
  651.  
  652.     /* silly TOS rules for matching attributes */
  653.         if (xattr.attr == 0) break;
  654.         if (xattr.attr & 0x21) break;
  655.         if (dta->dta_sattrib & xattr.attr)
  656.             break;
  657.     }
  658.  
  659. /* here, we have a match */
  660.     dta->dta_attrib = xattr.attr;
  661.     dta->dta_time = xattr.mtime;
  662.     dta->dta_date = xattr.mdate;
  663.     dta->dta_size = xattr.size;
  664.     strcpy(dta->dta_name, buf);
  665.  
  666.     if (curproc->domain == DOM_TOS && !(fs->fsflags & FS_CASESENSITIVE)) {
  667.         strupr(dta->dta_name);
  668.     }
  669.     return 0;
  670. }
  671.  
  672. long ARGS_ON_STACK
  673. f_attrib(name, rwflag, attr)
  674.     const char *name;
  675.     int rwflag;
  676.     int attr;
  677. {
  678.     fcookie fc;
  679.     XATTR xattr;
  680.     long r;
  681.  
  682.     TRACE(("Fattrib(%s, %d)", name, attr));
  683.  
  684.     r = path2cookie(name, follow_links, &fc);
  685.  
  686.     if (r) {
  687.         DEBUG(("Fattrib(%s): error %ld", name, r));
  688.         return r;
  689.     }
  690.  
  691.     r = (*fc.fs->getxattr)(&fc, &xattr);
  692.  
  693.     if (r) {
  694.         DEBUG(("Fattrib(%s): getxattr returned %ld", name, r));
  695.         release_cookie(&fc);
  696.         return r;
  697.     }
  698.  
  699.     if (rwflag) {
  700.         if (attr & (FA_LABEL|FA_DIR)) {
  701.             DEBUG(("Fattrib(%s): illegal attributes specified",name));
  702.             r = EACCDN;
  703.         } else if (curproc->euid && curproc->euid != xattr.uid) {
  704.             DEBUG(("Fattrib(%s): not the file's owner",name));
  705.             r = EACCDN;
  706.         } else if (xattr.attr & (FA_LABEL|FA_DIR)) {
  707.             DEBUG(("Fattrib(%s): file is a volume label "
  708.                   "or directory",name));
  709.             r = EACCDN;
  710.         } else {
  711.             r = (*fc.fs->chattr)(&fc, attr);
  712.         }
  713.         release_cookie(&fc);
  714.         return r;
  715.     } else {
  716.         release_cookie(&fc);
  717.         return xattr.attr;
  718.     }
  719. }
  720.  
  721. long ARGS_ON_STACK
  722. f_delete(name)
  723.     const char *name;
  724. {
  725.     fcookie dir;
  726.     long r;
  727.     char temp1[PATH_MAX];
  728.  
  729.     TRACE(("Fdelete(%s)", name));
  730.  
  731.     r = path2cookie(name, temp1, &dir);
  732.  
  733.     if (r) {
  734.         DEBUG(("Fdelete: error %ld", r));
  735.         return r;
  736.     }
  737.  
  738. /* check for write permission on directory */
  739.     r = dir_access(&dir, S_IWOTH);
  740.     if (r) {
  741.         DEBUG(("Fdelete(%s): write access to directory denied",name));
  742.     } else {
  743. /* BUG: we should check here for a read-only file */
  744.         r = (*dir.fs->remove)(&dir,temp1);
  745.     }
  746.     release_cookie(&dir);
  747.     return r;
  748. }
  749.  
  750. long ARGS_ON_STACK
  751. f_rename(junk, old, new)
  752.     int junk;        /* ignored, for TOS compatibility */
  753.     const char *old, *new;
  754. {
  755.     fcookie olddir, newdir, oldfil;
  756.     XATTR xattr;
  757.     char temp1[PATH_MAX], temp2[PATH_MAX];
  758.     long r;
  759.  
  760.     UNUSED(junk);
  761.  
  762.     TRACE(("Frename(%s, %s)", old, new));
  763.  
  764.     r = path2cookie(old, temp2, &olddir);
  765.     if (r) {
  766.         DEBUG(("Frename(%s,%s): error parsing old name",old,new));
  767.         return r;
  768.     }
  769. /* check for permissions on the old file
  770.  * GEMDOS doesn't allow rename if the file is FA_RDONLY
  771.  * we enforce this restriction only on regular files; processes,
  772.  * directories, and character special files can be renamed at will
  773.  */
  774.     r = relpath2cookie(&olddir, temp2, (char *)0, &oldfil, 0);
  775.     if (r) {
  776.         DEBUG(("Frename(%s,%s): old file not found",old,new));
  777.         release_cookie(&olddir);
  778.         return r;
  779.     }
  780.     r = (*oldfil.fs->getxattr)(&oldfil, &xattr);
  781.     release_cookie(&oldfil);
  782.     if (r ||
  783.         ((xattr.mode & S_IFMT) == S_IFREG && (xattr.attr & FA_RDONLY)) )
  784.     {
  785.         DEBUG(("Frename(%s,%s): access to old file not granted",old,new));
  786.         release_cookie(&olddir);
  787.         return EACCDN;
  788.     }
  789.     r = path2cookie(new, temp1, &newdir);
  790.     if (r) {
  791.         DEBUG(("Frename(%s,%s): error parsing new name",old,new));
  792.         release_cookie(&olddir);
  793.         return r;
  794.     }
  795.  
  796.     if (newdir.fs != olddir.fs) {
  797.         DEBUG(("Frename(%s,%s): different file systems",old,new));
  798.         release_cookie(&olddir);
  799.         release_cookie(&newdir);
  800.         return EXDEV;    /* cross device rename */
  801.     }
  802.  
  803. /* check for write permission on both directories */
  804.     r = dir_access(&olddir, S_IWOTH);
  805.     if (!r) r = dir_access(&newdir, S_IWOTH);
  806.     if (r) {
  807.         DEBUG(("Frename(%s,%s): access to a directory denied",old,new));
  808.     } else {
  809.         r = (*newdir.fs->rename)(&olddir, temp2, &newdir, temp1);
  810.     }
  811.     release_cookie(&olddir);
  812.     release_cookie(&newdir);
  813.     return r;
  814. }
  815.  
  816. /*
  817.  * GEMDOS extension: Dpathconf(name, which)
  818.  * returns information about filesystem-imposed limits; "name" is the name
  819.  * of a file or directory about which the limit information is requested;
  820.  * "which" is the limit requested, as follows:
  821.  *    -1    max. value of "which" allowed
  822.  *    0    internal limit on open files, if any
  823.  *    1    max. number of links to a file    {LINK_MAX}
  824.  *    2    max. path name length        {PATH_MAX}
  825.  *    3    max. file name length        {NAME_MAX}
  826.  *    4    no. of bytes in atomic write to FIFO {PIPE_BUF}
  827.  *    5    file name truncation rules
  828.  *    6    file name case translation rules
  829.  *
  830.  * unlimited values are returned as 0x7fffffffL
  831.  *
  832.  * see also Sysconf() in dos.c
  833.  */
  834.  
  835. long ARGS_ON_STACK
  836. d_pathconf(name, which)
  837.     const char *name;
  838.     int which;
  839. {
  840.     fcookie dir;
  841.     long r;
  842.  
  843.     r = path2cookie(name, (char *)0, &dir);
  844.     if (r) {
  845.         DEBUG(("Dpathconf(%s): bad path",name));
  846.         return r;
  847.     }
  848.     r = (*dir.fs->pathconf)(&dir, which);
  849.     if (which == DP_CASE && r == EINVFN) {
  850.     /* backward compatibility with old .XFS files */
  851.         r = (dir.fs->fsflags & FS_CASESENSITIVE) ? DP_CASESENS :
  852.                 DP_CASEINSENS;
  853.     }
  854.     release_cookie(&dir);
  855.     return r;
  856. }
  857.  
  858. /*
  859.  * GEMDOS extension: Opendir/Readdir/Rewinddir/Closedir offer a new,
  860.  * POSIX-like alternative to Fsfirst/Fsnext, and as a bonus allow for
  861.  * arbitrary length file names
  862.  */
  863.  
  864. long ARGS_ON_STACK
  865. d_opendir(name, flag)
  866.     const char *name;
  867.     int flag;
  868. {
  869.     DIR *dirh;
  870.     fcookie dir;
  871.     long r;
  872.  
  873.     r = path2cookie(name, follow_links, &dir);
  874.     if (r) {
  875.         DEBUG(("Dopendir(%s): error %ld", name, r));
  876.         return r;
  877.     }
  878.     r = dir_access(&dir, S_IROTH);
  879.     if (r) {
  880.         DEBUG(("Dopendir(%s): read permission denied", name));
  881.         release_cookie(&dir);
  882.         return r;
  883.     }
  884.  
  885.     dirh = (DIR *)kmalloc(SIZEOF(DIR));
  886.     if (!dirh) {
  887.         release_cookie(&dir);
  888.         return ENSMEM;
  889.     }
  890.  
  891.     dirh->fc = dir;
  892.     dirh->index = 0;
  893.     dirh->flags = flag;
  894.     r = (*dir.fs->opendir)(dirh, flag);
  895.     if (r) {
  896.         DEBUG(("d_opendir(%s): opendir returned %ld", name, r));
  897.         release_cookie(&dir);
  898.         kfree(dirh);
  899.         return r;
  900.     }
  901.  
  902. /* we keep a chain of open directories so that if a process
  903.  * terminates without closing them all, we can clean up
  904.  */
  905.     dirh->next = curproc->searches;
  906.     curproc->searches = dirh;
  907.  
  908.     return (long)dirh;
  909. }
  910.  
  911. long ARGS_ON_STACK
  912. d_readdir(len, handle, buf)
  913.     int len;
  914.     long handle;
  915.     char *buf;
  916. {
  917.     DIR *dirh = (DIR *)handle;
  918.     fcookie fc;
  919.     long r;
  920.  
  921.     if (!dirh->fc.fs)
  922.         return EIHNDL;
  923.     r = (*dirh->fc.fs->readdir)(dirh, buf, len, &fc);
  924.     if (r == 0)
  925.         release_cookie(&fc);
  926.     return r;
  927. }
  928.  
  929. long ARGS_ON_STACK
  930. d_rewind(handle)
  931.     long handle;
  932. {
  933.     DIR *dirh = (DIR *)handle;
  934.  
  935.     if (!dirh->fc.fs)
  936.         return EIHNDL;
  937.     return (*dirh->fc.fs->rewinddir)(dirh);
  938. }
  939.  
  940. /*
  941.  * NOTE: there is also code in terminate() in dosmem.c that
  942.  * does automatic closes of directory searches.
  943.  * If you change d_closedir(), you may also need to change
  944.  * terminate().
  945.  */
  946.  
  947. long ARGS_ON_STACK
  948. d_closedir(handle)
  949.     long handle;
  950. {
  951.     long r;
  952.     DIR *dirh = (DIR *)handle;
  953.     DIR **where;
  954.  
  955.     where = &curproc->searches;
  956.     while (*where && *where != dirh) {
  957.         where = &((*where)->next);
  958.     }
  959.     if (!*where) {
  960.         DEBUG(("Dclosedir: not an open directory"));
  961.         return EIHNDL;
  962.     }
  963.  
  964. /* unlink the directory from the chain */
  965.     *where = dirh->next;
  966.  
  967.     if (dirh->fc.fs) {
  968.         r = (*dirh->fc.fs->closedir)(dirh);
  969.         release_cookie(&dirh->fc);
  970.     } else {
  971.         r = 0;
  972.     }
  973.  
  974.     if (r) {
  975.         DEBUG(("Dclosedir: error %ld", r));
  976.     }
  977.     kfree(dirh);
  978.     return r;
  979. }
  980.  
  981. /*
  982.  * GEMDOS extension: Fxattr gets extended attributes for a file. "flag"
  983.  * is 0 if symbolic links are to be followed (like stat), 1 if not (like
  984.  * lstat).
  985.  */
  986.  
  987. long ARGS_ON_STACK
  988. f_xattr(flag, name, xattr)
  989.     int flag;
  990.     const char *name;
  991.     XATTR *xattr;
  992. {
  993.     fcookie fc;
  994.     long r;
  995.  
  996.     TRACE(("Fxattr(%d, %s)", flag, name));
  997.  
  998.     r = path2cookie(name, flag ? (char *)0 : follow_links, &fc);
  999.     if (r) {
  1000.         DEBUG(("Fxattr(%s): path2cookie returned %ld", name, r));
  1001.         return r;
  1002.     }
  1003.     r = (*fc.fs->getxattr)(&fc, xattr);
  1004.     if (r) {
  1005.         DEBUG(("Fxattr(%s): returning %ld", name, r));
  1006.     }
  1007.     release_cookie(&fc);
  1008.     return r;
  1009. }
  1010.  
  1011. /*
  1012.  * GEMDOS extension: Flink(old, new) creates a hard link named "new"
  1013.  * to the file "old".
  1014.  */
  1015.  
  1016. long ARGS_ON_STACK
  1017. f_link(old, new)
  1018.     const char *old, *new;
  1019. {
  1020.     fcookie olddir, newdir;
  1021.     char temp1[PATH_MAX], temp2[PATH_MAX];
  1022.     long r;
  1023.  
  1024.     TRACE(("Flink(%s, %s)", old, new));
  1025.  
  1026.     r = path2cookie(old, temp2, &olddir);
  1027.     if (r) {
  1028.         DEBUG(("Flink(%s,%s): error parsing old name",old,new));
  1029.         return r;
  1030.     }
  1031.     r = path2cookie(new, temp1, &newdir);
  1032.     if (r) {
  1033.         DEBUG(("Flink(%s,%s): error parsing new name",old,new));
  1034.         release_cookie(&olddir);
  1035.         return r;
  1036.     }
  1037.  
  1038.     if (newdir.fs != olddir.fs) {
  1039.         DEBUG(("Flink(%s,%s): different file systems",old,new));
  1040.         release_cookie(&olddir);
  1041.         release_cookie(&newdir);
  1042.         return EXDEV;    /* cross device link */
  1043.     }
  1044.  
  1045. /* check for write permission on the destination directory */
  1046.  
  1047.     r = dir_access(&newdir, S_IWOTH);
  1048.     if (r) {
  1049.         DEBUG(("Flink(%s,%s): access to directory denied",old,new));
  1050.     } else
  1051.         r = (*newdir.fs->hardlink)(&olddir, temp2, &newdir, temp1);
  1052.     release_cookie(&olddir);
  1053.     release_cookie(&newdir);
  1054.     return r;
  1055. }
  1056.  
  1057. /*
  1058.  * GEMDOS extension: Fsymlink(old, new): create a symbolic link named
  1059.  * "new" that contains the path "old".
  1060.  */
  1061.  
  1062. long ARGS_ON_STACK
  1063. f_symlink(old, new)
  1064.     const char *old, *new;
  1065. {
  1066.     fcookie newdir;
  1067.     long r;
  1068.     char temp1[PATH_MAX];
  1069.  
  1070.     TRACE(("Fsymlink(%s, %s)", old, new));
  1071.  
  1072.     r = path2cookie(new, temp1, &newdir);
  1073.     if (r) {
  1074.         DEBUG(("Fsymlink(%s,%s): error parsing %s", old,new,new));
  1075.         return r;
  1076.     }
  1077.     r = dir_access(&newdir, S_IWOTH);
  1078.     if (r) {
  1079.         DEBUG(("Fsymlink(%s,%s): access to directory denied",old,new));
  1080.     } else
  1081.         r = (*newdir.fs->symlink)(&newdir, temp1, old);
  1082.     release_cookie(&newdir);
  1083.     return r;
  1084. }
  1085.  
  1086. /*
  1087.  * GEMDOS extension: Freadlink(buflen, buf, linkfile):
  1088.  * read the contents of the symbolic link "linkfile" into the buffer
  1089.  * "buf", which has length "buflen".
  1090.  */
  1091.  
  1092. long ARGS_ON_STACK
  1093. f_readlink(buflen, buf, linkfile)
  1094.     int buflen;
  1095.     char *buf;
  1096.     const char *linkfile;
  1097. {
  1098.     fcookie file;
  1099.     long r;
  1100.     XATTR xattr;
  1101.  
  1102.     TRACE(("Freadlink(%s)", linkfile));
  1103.  
  1104.     r = path2cookie(linkfile, (char *)0, &file);
  1105.     if (r) {
  1106.         DEBUG(("Freadlink: unable to find %s", linkfile));
  1107.         return r;
  1108.     }
  1109.     r = (*file.fs->getxattr)(&file, &xattr);
  1110.     if (r) {
  1111.         DEBUG(("Freadlink: unable to get attributes for %s", linkfile));
  1112.     } else if ( (xattr.mode & S_IFMT) == S_IFLNK )
  1113.         r = (*file.fs->readlink)(&file, buf, buflen);
  1114.     else {
  1115.         DEBUG(("Freadlink: %s is not a link", linkfile));
  1116.         r = EACCDN;
  1117.     }
  1118.     release_cookie(&file);
  1119.     return r;
  1120. }
  1121.  
  1122. /*
  1123.  * GEMDOS extension: Dcntl(): do file system specific functions
  1124.  */
  1125.  
  1126. long ARGS_ON_STACK
  1127. d_cntl(cmd, name, arg)
  1128.     int cmd;
  1129.     const char *name;
  1130.     long arg;
  1131. {
  1132.     fcookie dir;
  1133.     long r;
  1134.     char temp1[PATH_MAX];
  1135.  
  1136.     TRACE(("Dcntl(cmd=%x, file=%s, arg=%lx)", cmd, name, arg));
  1137.  
  1138.     r = path2cookie(name, temp1, &dir);
  1139.     if (r) {
  1140.         DEBUG(("Dcntl: couldn't find %s", name));
  1141.         return r;
  1142.     }
  1143.     r = (*dir.fs->fscntl)(&dir, temp1, cmd, arg);
  1144.     release_cookie(&dir);
  1145.     return r;
  1146. }
  1147.  
  1148. /*
  1149.  * GEMDOS extension: Fchown(name, uid, gid) changes the user and group
  1150.  * ownerships of a file to "uid" and "gid" respectively.
  1151.  */
  1152.  
  1153. long ARGS_ON_STACK
  1154. f_chown(name, uid, gid)
  1155.     const char *name;
  1156.     int uid, gid;
  1157. {
  1158.     fcookie fc;
  1159.     XATTR xattr;
  1160.     long r;
  1161.  
  1162.     TRACE(("Fchown(%s, %d, %d)", name, uid, gid));
  1163.  
  1164.     r = path2cookie(name, follow_links, &fc);
  1165.     if (r) {
  1166.         DEBUG(("Fchown(%s): error %ld", name, r));
  1167.         return r;
  1168.     }
  1169.  
  1170. /* MiNT acts like _POSIX_CHOWN_RESTRICTED: a non-privileged process can
  1171.  * only change the ownership of a file that is owned by this user, to
  1172.  * the effective group id of the process
  1173.  */
  1174.     if (curproc->euid) {
  1175.         if (curproc->egid != gid)
  1176.             r = EACCDN;
  1177.         else
  1178.             r = (*fc.fs->getxattr)(&fc, &xattr);
  1179.         if (r) {
  1180.             DEBUG(("Fchown(%s): unable to get file attributes",name));
  1181.             release_cookie(&fc);
  1182.             return r;
  1183.         }
  1184.         if (xattr.uid != curproc->euid || xattr.uid != uid) {
  1185.             DEBUG(("Fchown(%s): not the file's owner",name));
  1186.             release_cookie(&fc);
  1187.             return EACCDN;
  1188.         }
  1189.     }
  1190.     r = (*fc.fs->chown)(&fc, uid, gid);
  1191.     release_cookie(&fc);
  1192.     return r;
  1193. }
  1194.  
  1195. /*
  1196.  * GEMDOS extension: Fchmod(file, mode) changes a file's access
  1197.  * permissions.
  1198.  */
  1199.  
  1200. long ARGS_ON_STACK
  1201. f_chmod(name, mode)
  1202.     const char *name;
  1203.     unsigned mode;
  1204. {
  1205.     fcookie fc;
  1206.     long r;
  1207.     XATTR xattr;
  1208.  
  1209.     TRACE(("Fchmod(%s, %o)", name, mode));
  1210.     r = path2cookie(name, follow_links, &fc);
  1211.     if (r) {
  1212.         DEBUG(("Fchmod(%s): error %ld", name, r));
  1213.         return r;
  1214.     }
  1215.     r = (*fc.fs->getxattr)(&fc, &xattr);
  1216.     if (r) {
  1217.         DEBUG(("Fchmod(%s): couldn't get file attributes",name));
  1218.     }
  1219.     else if (curproc->euid && curproc->euid != xattr.uid) {
  1220.         DEBUG(("Fchmod(%s): not the file's owner",name));
  1221.         r = EACCDN;
  1222.     } else {
  1223.         r = (*fc.fs->chmode)(&fc, mode & ~S_IFMT);
  1224.         if (r) DEBUG(("Fchmod: error %ld", r));
  1225.     }
  1226.     release_cookie(&fc);
  1227.     return r;
  1228. }
  1229.  
  1230. /*
  1231.  * GEMDOS extension: Dlock(mode, dev): locks or unlocks access to
  1232.  * a BIOS device. "mode" bit 0 is 0 for unlock, 1 for lock; "dev" is a
  1233.  * BIOS device (0 for A:, 1 for B:, etc.).
  1234.  *
  1235.  * Returns: 0 if the operation was successful
  1236.  *          EACCDN if a lock attempt is made on a drive that is being
  1237.  *            used
  1238.  *        ELOCKED if the drive is locked by another process
  1239.  *        ENSLOCK if a program attempts to unlock a drive it
  1240.  *            hasn't locked.
  1241.  * ++jr: if mode bit 1 is set, then instead of returning ELOCKED the
  1242.  * pid of the process which has locked the drive is returned (unless
  1243.  * it was locked by pid 0, in which case ELOCKED is still returned).
  1244.  */
  1245.  
  1246. PROC *dlockproc[NUM_DRIVES];
  1247.  
  1248. long ARGS_ON_STACK
  1249. d_lock(mode, dev)
  1250.     int mode, dev;
  1251. {
  1252.     PROC *p;
  1253.     FILEPTR *f;
  1254.     int i;
  1255.     extern int aliasdrv[];
  1256.  
  1257.     TRACE(("Dlock(%x,%c:)", mode, dev+'A'));
  1258.     if (dev < 0 || dev >= NUM_DRIVES) return EDRIVE;
  1259.     if (aliasdrv[dev]) {
  1260.         dev = aliasdrv[dev] - 1;
  1261.         if (dev < 0 || dev >= NUM_DRIVES)
  1262.             return EDRIVE;
  1263.     }
  1264.     if ( (mode&1) == 0) {    /* unlock */
  1265.         if (dlockproc[dev] == curproc) {
  1266.             dlockproc[dev] = 0;
  1267.             changedrv(dev);
  1268.             return 0;
  1269.         }
  1270.         DEBUG(("Dlock: no such lock"));
  1271.         return ENSLOCK;
  1272.     }
  1273.  
  1274. /* code for locking */
  1275. /* is the drive already locked? */
  1276.     if (dlockproc[dev]) {
  1277.         DEBUG(("Dlock: drive already locked"));
  1278.         if (dlockproc[dev] == curproc) return 0;
  1279.         if (dlockproc[dev]->pid == 0) return ELOCKED;
  1280.         return (mode & 2) ? dlockproc[dev]->pid : ELOCKED;
  1281.     }
  1282. /* see if the drive is in use */
  1283.     for (p = proclist; p; p = p->gl_next) {
  1284.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  1285.             continue;
  1286.         for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
  1287.             if ( ((f = p->handle[i]) != 0) && (f->fc.dev == dev) ) {
  1288.         DEBUG(("Dlock: process %d has an open handle on the drive", p->pid));
  1289.                 if (p->pid == 0) return EACCDN;
  1290.                 return (mode & 2) ? p->pid : EACCDN;
  1291.             }
  1292.         }
  1293.     }
  1294.  
  1295. /* if we reach here, the drive is not in use */
  1296. /* we lock it by setting dlockproc and by setting all root and current
  1297.  * directories referring to the device to a null file system
  1298.  */
  1299.     for (p = proclist; p; p = p->gl_next) {
  1300.         for (i = 0; i < NUM_DRIVES; i++) {
  1301.             if (p->root[i].dev == dev) {
  1302.                 release_cookie(&p->root[i]);
  1303.                 p->root[i].fs = 0;
  1304.             }
  1305.             if (p->curdir[i].dev == dev) {
  1306.                 release_cookie(&p->curdir[i]);
  1307.                 p->curdir[i].fs = 0;
  1308.             }
  1309.         }
  1310.     }
  1311.  
  1312.     dlockproc[dev] = curproc;
  1313.     return 0;
  1314. }
  1315.